<?php
namespace ESTool;
/**
 * ES 查询组装工具类
 * @author xianfei@leju.com
 * @version 0.1 测试版
 */
class Elasticsearch {
    
    static $_TIMEFORMAT;
    protected $post_content = array();
    private  $_aggs = array();
    protected $_fieldpre="";
    protected $timeFormat = '24h'; // 默认聚合时间间隔
    protected $_index = '';
    protected $_mapping = '';
    protected $_node = 'test'; // ES 请求数据节点
    private $_resultgroup = array();
    private $_result = array();
    private $_param = array();
    protected $_column = array();
    static public $CONF_MAP = array(
        'index' => array(
            'esf_bi_access_event_res' => 'esf_bi_access_event_res',
            'esf_bi_access_res' => 'esf_bi_access_res',
            'esf_bi_house_res_test' => 'esf_bi_house_res',
            'esf_bi_house_op' => 'esf_bi_house_res',
        ),
    );
    // 单例对象
    private static $_selfObj = null;
    private function __construct(){}
    private function __clone(){}
    public static function getInstance() {
        if(!isset(self::$_selfObj) || !self::$_selfObj instanceof self) {
            self::$_selfObj = new self();
        }
        return self::$_selfObj;
    }
    
    public static function getInstanceNew() {
    	return new self();
    }  
    /**
     * 设置ES请求节点
     * @param unknown $node
     */
    public function setNode($node) {
        $this->_node = $node; 
        return $this;
    }
    /**
     * 获取当前请求节点
     */
    public function getNode() {
        return $this->_node;
    }
    public function setIndexMapping($index, $mapping='') {
        $count = 1;
        $index = str_replace(array_keys(Elasticsearch::$CONF_MAP['index']), array_values(Elasticsearch::$CONF_MAP['index']), $index, $count);
        $this->_index = $index;
        $this->_mapping = $mapping;
        return $this;
    }
    public function getMapping($index) {
        $result = Elasticsearchpool::getMapping($index);
        return $result;
    }

    /**
     * 保存数据
     * @param array $data 数据
     * @param array $id_filed 唯一字段组合
     * @param array $limit 单次插入数据条数
     * 
     */
    public function save($data,$id_filed=array("city", "id"),$limit=3000) {
        return Elasticsearchpool::save($data, $this->_index, $this->_mapping, $id_filed,$limit, $this->_node);
    }
    public function update($data,$id_filed=array("city", "id"),$limit=3000) {
        return Elasticsearchpool::update($data, $this->_index, $this->_mapping, $id_filed,$limit, $this->_node);
    }
    /**
     * 设置参数
     * @param array $where 查询条件
     * @param array $column    查询聚合列
     * @param array $columnFamily    （聚合字段）查询列族
     * @param array $rowkey    （聚合字段）唯一索引 rowkey
     * @param string $index    查询数据索引
     * @param string $mapping    索引mapping
     * @param string $timeFormat    时间聚合维度 默认24h一天
     * @param string $field_pre    数据字段前缀
     * @param array $orderfields    排序字段
     * @return Es_Elasticsearch 查询对象
     */ 
    public function setParam($where, $column, $columnFamily, $rowkey, $index='', $mapping='', $timeFormat='', $field_pre='',$orderfields='') {
        /* 
        Debug::op_memory_set_query(__FILE__, __LINE__, 
            '[where]'  .  "\n" . json_encode($where),
            '[column]'  .  "\n" . json_encode($column),
            '[columnFamily]'  .  "\n" . json_encode($columnFamily),
            '[rowkey]'  .  "\n" . json_encode($rowkey),
            '[index]'  .  "\n" . json_encode($index),
            '[mapping]'  .  "\n" . json_encode($mapping),
            '[timeFormat]'  .  "\n" . json_encode($timeFormat),
            '[field_pre]'  .  "\n" . json_encode($field_pre)
        ); 
        */
        $this->setIndexMapping($index, $mapping);
        $this->setFieldPre($field_pre);
        $this->setColumn($column);

        $this->setTimeFormat($where, $timeFormat);
        
        $this->setWhere($where);
        $this->setGroup($rowkey);
        $this->setGroupfield($columnFamily);
        $this->setResultGroup($rowkey, $columnFamily);
        $this->setOrder($orderfields);
        return $this;
    }
    /**
     * 获取聚合字段
     */
    public function getResultGroup() {
        return $this->_resultgroup;
    }
    /**
     * 设置返回字段
     * @param array $fields
     * @return Elasticsearch
     */
    public function setSource($fields=array()) {
        foreach ($fields as $f) {
            $this->post_content['_source'][] = $this->_fieldpre . $f;
        }
        return $this;
    }
    
    
    
    /**
     * 脚本评分~设置字段权重
     * @link https://www.elastic.co/guide/cn/elasticsearch/guide/current/script-score.html
     * @param string $field 查询字段
     * @param array $tagList 关键词=>权重
     * @return Elasticsearch
     * "script": {
            "lang": "painless",
            "params": {
                "param1": value1,
                "param2": value2
            },
            "inline": "_score * doc['my_numeric_field'].value / Math.pow(params.param1, params.param2)"
        }
     */
    public function setScoreFieldsWight($fieldsWight) {
        $params = array();
        $script = "_score";
        foreach($fieldsWight as $f => $wight) {
            $script .= " + doc['".$f."'].value * ".$wight;
        }
        $query = $this->post_content['query'];
        $this->post_content['query'] = array();
        $this->post_content['query']['function_score']['query'] = $query;
        $this->post_content['query']['function_score']['script_score']['script'] = array(
            "lang" => "painless",
            "inline" => $script,
        );
        return $this;
    }
    /**
     * 设置关键词查询权重
     * @param string $field 查询字段
     * @param array $tagList 关键词=>权重
     * @return Elasticsearch
     */
    public function setScoreTagWight($field, $tagList) {
        foreach ($tagList as $val => $weight) {
            $weightList[] = array(
                "weight" => $weight,
                "filter" => array(
                    "term" => array(
                        $field => $val
                    )
                )
            );
        }
        $this->post_content['query']['function_score'] = array(
            'query' => $where,
            'functions' => $weightList
        );
        return $this;
    }
    /**
     * 设置查询结果，详细信息返回数据条数
     */
    public function setSize($size=0) {
        $this->post_content['size'] = intval($size);
        return $this;
    }
    /**
     * 设置查询结果，详细信息返回数据 偏移量
     */
    public function setFrom($from=0) {
        $this->post_content['from'] = intval($from);
        return $this;
    }
    /**
     * 设置时间聚合维度
     * @param string $timeFormat
     */
    public function setTimeFormat($where, $timeFormat='') {
        if(empty($timeFormat) && isset($where['time'])) {
            $hour = round((strtotime(max($where['time'])) - strtotime(min($where['time']))) / 3600);
            $this->timeFormat = round($hour / 48, 2);
            $this->timeFormat > 24 && $this->timeFormat = 24;
            $this->timeFormat .= 'h';
        } else {
            $this->timeFormat = floatval($timeFormat) . 'h';
            $timeFormat = strtolower($timeFormat);
            foreach (array('m','h','d') as $fm) {
                if (strstr($timeFormat, $fm) !== false) {
                    $this->timeFormat = floatval($timeFormat) . $fm;
                    break;
                }
            }
            
        }
        if ($this->timeFormat <= 0) {
            $this->timeFormat = '24h';
        }
        return $this;
    }
    /**
     * 设置字段前缀
     * @param string $fieldpre
     * @return Es_Elasticsearch
     */
    public function setFieldPre($fieldpre) {
        $this->_fieldpre = $fieldpre;
        return $this;
    }
    /**
     * 设置 查询字段
     * @param array $column
     * @return Es_Elasticsearch
     */
    private function setColumn($column) {
        foreach ($column as $f => $c) {
            if (is_array($c) && !isset($c['type']) && empty($c['type'])) {
                $c['type'] = 'cardinality';
            }
            
            if (is_array($c) && !isset($c['field']) && !is_numeric($f)) {
                $c['field'] = $f;
            }
            
//             if (is_numeric($f)){
//                 if(!is_array($c)) {
//                     $f = $c;
//                 }
//             }
            $this->_column[$f] = $c;
        }
        return $this;
    }
    /**
     * 设置排序字段
     * @param array $orderfields
     * @return Es_Elasticsearch
     */
    public function setOrder($orderfields=array()) {
        //empty($orderfields) && $orderfields = $this->_resultgroup;
        if (!empty($orderfields)) {
            foreach ($orderfields as $field => $order) {
                if (is_array($order)) {
                    $order = '';
                }
                $order = strtolower($order);
                if (strstr($field, 'geo')===false) {
                    $this->post_content['sort'][$this->_fieldpre.$field] = array(
                        'order' => in_array($order, array('desc', 'asc')) ? $order : 'desc',
                    );
                }
            }
        }
        return $this;
    }
    /**
     * 设置返回结果集，需要的分组字段
     * @param array $rowkey
     * @param array $columnFamily
     * @param array $column
     * @return Es_Elasticsearch
     */
    public function setResultGroup($rowkey, $columnFamily) {
        if (is_array($rowkey)) {
            foreach ($rowkey as $g => $gv) {
                if (!is_array($gv)) {
                    $g = $gv;
                } elseif(isset($gv['show_field'])) {
                    $g = $gv['show_field'];
                }
                $this->_resultgroup[$g] = '';
            }
        }
        if (is_array($columnFamily)) {
            foreach ($columnFamily as $cfk => $cfv) {
                if (is_array($cfv)) {
                    isset($cfv['field']) && $cfk = $cfv['field'];
                    isset($cfv['show_field']) && $cfk = $cfv['show_field'];
                } else {
                    $cfk = $cfv;
                }
                foreach ($this->_column as $clk => $clv) {
                    if (is_array($clv) && isset($clv['field'])) {
                        $clv = isset($clv['show_field']) ? $clv['show_field'] : $clv['field'];
                    } elseif (is_array($clv)) {
                        $clv = $clk;
                    }
                    $this->_resultgroup[$cfk][$clk] = $clv;
                }
            }
        }
        return $this;
    }
    
    
	/**
	 * 设置查询条件
	 * @param array $where 查询条件
	 * return $post_content 带有查询条件和时间的  request body
	 */
	public function setWhere($where) {
	    $this->post_content["query"] = array();    
	    
	    if (empty($where)) {
	        return $this;
	    }
	    $this->where = $where;
	    // 格式化查询时间
	    foreach ($where as $k => $v) {
	        if ($k!="first_time_date" && $k!="frist_time_date" && $k!="frist_time" && $k!="update_time" && strstr($k , 'time')!== false) {
	            if (is_array($v)) {
    	            if (max($v) > 1400000000) {
    	                $where[$k] = array(date("Y-m-d\TH:i",min($v)), date("Y-m-d\TH:i",max($v)));
    	            } else {
    	                $where[$k] = array(date("Y-m-d\TH:i",strtotime(min($v))), date("Y-m-d\TH:i",strtotime(max($v))));
    	            }
	            } else {
	                if (intval($v) > 1400000000) {
	                    $where[$k] = date("Y-m-d\TH:i",$v);
	                } else {
	                    $where[$k] = date("Y-m-d\TH:i",strtotime($v));
	                }
	            }
	        }
	    }
	    foreach ($where as $field => $val) {
	        $field = explode(",", $field);
	        $type = '';
	        if (isset($field[1])) {
	            $type = $field[1];
	        }
	        $type = strtolower($type);
	        switch ($type) {
	            case 'geo_distance':
	                is_array($val['location']) || $val['location'] = explode(",", $val['location']);
	                $val['location'][0] = floatval($val['location'][0]);
	                $val['location'][1] = floatval($val['location'][1]);
	                empty($val['distance']) && $val['distance'] = '5km'; 
	                $val['distance'] = strtolower($val['distance']);
	                $where_val = array(
                        'geo_distance'=> array(
                            !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => $val['location'],
                            "distance"=> $val['distance'],
	                    )
	                );
	                $this->post_content["query"]['bool']['filter'][] = $where_val;
	                break;
	            case 'geo_bounding_box':
	                list($k, $pos_1) = each($val);
	                list($k, $pos_2) = each($val);
	                is_array($pos_1) || $pos_1 = explode(",", $pos_1);
	                is_array($pos_2) || $pos_2 = explode(",", $pos_2);
	                $top_left = $bottom_right = array();
	                $top_left[0] = floatval(min(array($pos_1[0],$pos_2[0])));
	                $bottom_right[0] = floatval(max(array($pos_1[0],$pos_2[0])));
	                $top_left[1] = floatval(max(array($pos_1[1],$pos_2[1])));
	                $bottom_right[1] = floatval(min(array($pos_1[1],$pos_2[1])));
	                $where_val = array(
	                    'geo_bounding_box'=> array(
        	                !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => array(
                                "top_left"=> $top_left,
                                "bottom_right"=> $bottom_right
                            )
    	                )
	                );
	                $this->post_content["query"]['bool']['filter'][] = $where_val;
	                break;
                case 'like' :
                    $where_val = array("wildcard" => array(
                        !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => str_replace("%","*",$val)
                    ));
                    $this->post_content["query"]['bool']['filter'][] = $where_val;
                    break;
                    
                case 'match' :
                    $where_val = array($type => array(
                    !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => $val
                    ));
                    $this->post_content["query"]['bool']['must'][] = $where_val;
                    break;
                case 'prefix' :
                    $where_val = array($type => array(
                        !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => $val
                    ));
                    $this->post_content["query"]['bool']['filter'][] = $where_val;
                    break;
	            case 'in' :
	                if (!is_array($val)) {
	                    $val = explode(",", $val);
	                }
	                $val = array_values((array)$val);
	                $where_val = array("terms" => array(
	                    !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => $val
	                ));
	                $this->post_content["query"]['bool']['filter'][] = $where_val;
	                break;
                case 'not in' :
                    $val = array_values((array)$val);
                    $where_val = array("terms" => array(
                        !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => $val
                    ));
                    $this->post_content["query"]['bool']['must_not'][] = $where_val;
                    break;
	            case 'neq' :
	                $where_val = array("term" => array(
	                    !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => $val
	                ));
	                $this->post_content["query"]['bool']['must_not'][] = $where_val;
	                break;
	            case 'gte' :
	            case 'gt' :
	            case 'lte' :
	            case 'lt' :
	                $where_val = array("range" => array(
	                !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => array(
	                   strtolower($type) => $val,
	                )));
	                $this->post_content["query"]['bool']['filter'][] = $where_val;
	                break;
	            default:
	                !is_array($val) && strstr($val, ",") !==false &&  $val = explode(",", $val);
	                if (is_array($val) && count($val) == 2) {
	                    $where_val = array('range' => array(
	                        !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => array(
	                            'gte' => min($val),
	                            'lt' => max($val)
	                        )
	                    ));
	                } elseif (is_array($val)) {
	                    $where_val = array('terms' => array(
	                        !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => array_values($val)
	                    ));
	                } else {
	                    $where_val = array("term" => array(
	                        !empty($this->_fieldpre) ? $this->_fieldpre . $field[0] : $field[0] => $val
	                    ));
	                }
	                $this->post_content["query"]['bool']['filter'][] = $where_val;
	                break;
	        }
	    }
	    
	    return $this;
	}
	/**
	 * 设置分组字段
	 * @param array $group
	 * @return Es_Elasticsearch
	 */
	public function setGroup($group) {
	    if (empty($group)) {
	        return $this;
	    }
	    $this->_aggs = & $this->post_content;
	    foreach ($group as $k => $field) {
	        if (is_array($field)) {
    	        !isset($field['field']) && $field['field'] = $k;
	        }
	        $this->_setGroup($this->_aggs, $field);
	        if (is_array($field)) {
	            isset($field['show_field']) && $field = $field['show_field'];
	            isset($field['field']) && $field = $field['field'];
	        }
	        $this->_aggs = &$this->_aggs['aggs'][$field . 'list'];
	    }

	    return $this;
	}
	/**
	 * 设置分组统计字段
	 * @param array $columnFamily
	 * @param array $column
	 * @return Es_Elasticsearch
	 */
	public function setGroupfield($columnFamily) {
	    empty($this->_aggs) && $this->_aggs = & $this->post_content;
	    foreach ($columnFamily as $k => $field) {
	        if (is_array($field)) {
    	        !isset($field['field']) &&  $field['field'] = $k;
	        }
	        $this->_setGroup($this->_aggs, $field);
	        if (is_array($field)) {
	            isset($field['show_field']) && $field = $field['show_field'];
	            isset($field['field']) && $field = $field['field'];
	        }
	        foreach ($this->_column as $f => $col) {
	            if($col == 'pv'){
	                continue;
	            }
	            if (is_numeric($f) && !isset($col['field'])) {
	                exit("ES aggs filed is null!");
	            }
	            if (is_numeric($f) && isset($col['field'])) {
	                $col['show_field'] = isset($col['show_field']) ? $col['show_field'] : $col['field'];
	            } elseif (!is_numeric($f) && !is_array($col)) {
	                $tmp = $col;
	                $col = array();
	                $col['show_field'] = $tmp;
	                $col['field'] = $f;
	                $col['type'] = 'cardinality';
	            }
	            
	            $this->_aggs['aggs'][$field . 'list']['aggs'][$col['show_field']] = array(
	                $col['type'] => array(
	                    'field' => !empty($this->_fieldpre) ? $this->_fieldpre . $col['field'] : $col['field']
	                )
	            );
	        }
	        
	        $this->_aggs = & $this->_aggs['aggs'][$field . 'list'];
	    }
	    return $this;
	}
	/**
	 * 设置分组-内部方法
	 * @param array $aggs    聚合字段数据
	 * @param array $field    查询展示字段
	 * @param int $max_size 最大查询条数
	 */
	protected function _setGroup(&$aggs, $field, $max_size=100) {
	    
	    if (is_array($field) && isset($field['show_field'])) {
	        $show_field = $field['show_field'];
	    } elseif (is_array($field)) {
	        $show_field = $field['field'];
	    } else {
	        $show_field = $field;
	    }
	    if ((is_array($field) && strstr($field['field'],'geo')!==false) || (!is_array($field) && strstr($field,'geo')!==false)) {
	        if (!is_array($field)) {
	            $aggs['aggs'][$show_field . 'list'] = array(
	                "geohash_grid"=> array(
	                    "field"=> !empty($this->_fieldpre) ? $this->_fieldpre . $field : $field,
	                    "precision"=> 5
	                ),
	                "aggs" => array(
	                    "cell" => array(
	                        "geo_bounds" => array(
	                            "field"=> !empty($this->_fieldpre) ? $this->_fieldpre . $field : $field,
	                        )
	                    )
	                )
	            );
	        } elseif ($field['type'] == 'grid') {
	            isset($field['precision']) || $field['precision'] = 5;
	            $aggs['aggs'][$field['field'] . 'list'] = array(
	                "geohash_grid"=> array(
	                    "field"=> !empty($this->_fieldpre) ? $this->_fieldpre . $field['field'] : $field['field'],
	                    "precision"=> floatval($field['precision'])
	                ),
	                "aggs" => array(
	                    "cell" => array(
	                        "geo_bounds" => array(
	                            "field"=> !empty($this->_fieldpre) ? $this->_fieldpre . $field['field'] : $field['field'],
	                        )
	                    )
	                )
	            );
	        } elseif ($field['type'] == 'distance') {
	            isset($field['ranges']) || $field['ranges'][] = array(0,2);
	            isset($field['origin']) || $field['origin'] = array( 'lon' => 116.395645, 'lat' => 39.929986);
	            if (!is_array($field['origin'])) {
	                $temp = array();
	                $temp = explode(",", $field['origin']);
	                $field['origin'] = array( 'lon' => floatval($temp[0]), 'lat' => floatval($temp[1]));
	            }
	            if (!is_array($field['ranges'])) {
	                $field['ranges'] = explode(',', $field['ranges']);
	                sort($field['ranges']);
	                $temp = array();
	                foreach ($field['ranges'] as $range) {
	                    if (empty($temp)) {
	                        $temp[] = array(0, floatval($range));
	                    } else {
	                        $end = end($temp);
	                        $temp[] = array(floatval($end[1]), $range);
	                    }
	                }
	                $field['ranges'] = $temp;
	            }
	            foreach ($field['ranges'] as & $ranges) {
	                $ranges = array("from"=>floatval(min($ranges)), "to"=>floatval(max($ranges)));
	            }
	            $aggs['aggs'][$field['field'] . 'list'] = array(
	                "geo_distance"=>array(
	                    "field" => !empty($this->_fieldpre) ? $this->_fieldpre . $field['field'] : $field['field'],
	                    "unit" => "km",
	                    "origin" => $field['origin'],
	                    "ranges" => $field['ranges'],
	                )
	            );
	        }
	
	    } elseif (isset($field['type']) && $field['type'] == 'range') {
	        $ranges = $field['ranges'];
	        $field = $field['field'];
	        $aggs['aggs'][$show_field . 'list'] = array(
	            'range' => array(
	                'field' => !empty($this->_fieldpre) ? $this->_fieldpre . $field : $field,
	                "ranges" => $ranges,
	            )
	        );
	    } elseif ( ( empty($field['type']) ||  in_array($field['type'], array('date','day'))) &&(is_array($field) && strstr($field['field'],'time')!==false) || (!is_array($field) && strstr($field,'time')!==false)) {
	        $timeFormat = '24h';
	        $this->timeFormat && $timeFormat = $this->timeFormat;
	        if (is_array($field)) {
	            $timeFormat = isset($field['timeFormat']) && floatval($field['timeFormat']) ? $field['timeFormat'] : $this->timeFormat;
    	        $field = $field['field'];
	        }
	        $aggs['aggs'][$show_field . 'list'] = array(
	            'date_histogram' => array(
	                'field' => !empty($this->_fieldpre) ? $this->_fieldpre . $field : $field,
	                'interval' =>  $timeFormat,
	                //'time_zone' => 'Asia/Shanghai',
	                'min_doc_count' => 0,
	            )
	        );
	    } elseif (is_array($field)) {
	        $size = isset($field['size']) && !empty($field['size']) ? $field['size'] : $max_size;
    	    $order = isset($field['order']) && !empty($field['order']) ? $field['order'] : 'asc';
    	    $field = $field['field'];
    	    
	        $aggs['aggs'][$show_field . 'list'] = array(
	            'terms' => array(
	                'size' => $size,
	                'field' => !empty($this->_fieldpre) ? $this->_fieldpre . $field : $field,
	                'order' => array(
	                    // array("_term" => "desc"), // 聚合文本 排序
	                    array("_count" => "desc"), // 聚合统计数值 排序
	                ),
	            )
	        );
	    } else {
	        $aggs['aggs'][$show_field . 'list'] = array(
	            'terms' => array(
	                'size' => $max_size,
	                'field' => !empty($this->_fieldpre) ? $this->_fieldpre . $field : $field,
	                'order' => array(
	                    // array("_term" => "desc"), // 聚合文本 排序
	                    array("_count" => "desc"), // 聚合统计数值 排序
	                 ),
	            )
	        );
	        
	    }
	}
	
	/**
	 * 获取 表格二维数据
	 * @return Ambigous <string, boolean, multitype:multitype: , 返回查询的所有数据数组, unknown>|NULL|Ambigous <multitype:multitype:NULL, multitype:Ambigous <unknown_type, multitype:NULL number unknown multitype:NULL  > >
	 */
	public function fetchTable() {
	    if (strstr($this->_index,'esf_ol_house') !== false) {
	        return $this->fetchTableByMysql();
	    }
	    if (! $this->_result) {
	        return null;
	    }
	    $data = json_decode($this->_result, true);
	    if (!isset($data['aggregations'])) {
	        return null;
	    }
	    Debug::op_memory_set(__FILE__, __LINE__, 'fetchTable');
	    $result = $this->fetchItem($data['aggregations'], $this->_resultgroup);
	    Debug::op_memory_set(__FILE__, __LINE__, 'fetchTable');
	    return $result;
	}
	public function fetchTableSource() {
	    if (! $this->_result) {
	        return null;
	    }
	    $data = json_decode($this->_result, true);
	    if (!isset($data['hits']) || ! $data['hits']['total']) {
	        return array("total"=>0,"list"=>array());
	    }
	    Debug::op_memory_set(__FILE__, __LINE__, 'fetchTableSource');
	    $result = array();
	    $result['total'] = $data['hits']['total'];
	    $fieldpre = trim($this->_fieldpre, ".");
	    if (defined('DEBUG_DXF') && DEBUG_DXF) {
	        p($data['hits']);
	    }
	    foreach ($data['hits']['hits'] as $item) {
	        $data = array();
	        if ( ! empty($fieldpre)) {
    	        $data = $item['_source'][$fieldpre];
	        } else {
	            $data = $item['_source'];
	        }
	        if (isset($item['_score'])) {
	            $data['_score'] = $item['_score'];
	            
	        }
	        if (isset($this->post_content['_source']) && !empty($this->post_content['_source'])) {
    	        foreach ($this->post_content['_source'] as $f) {
    	            $f = str_replace($this->_fieldpre, "", $f);
    	            $row[$f] = "";
    	            isset($data[$f]) && $row[$f] = $data[$f];
    	        }
	        } else {
	            $row = $data;
	        }
	        foreach ($row as $f => $rv) {
	            if (is_array($rv)) {
	                $row[$f] = json_encode($rv);
	            }
	        }
	        $result['list'][] = $row;
	    }
	    Debug::op_memory_set(__FILE__, __LINE__, 'fetchTableSource');
	    return $result;
	}
	/**
	 * 获取 表格二维数据 - 内部方法
	 * @param unknown_type $soure
	 * @param unknown_type $group
	 * @param unknown_type $item_def
	 * @param unknown_type $index
	 * @return multitype:multitype:NULL number unknown multitype:NULL
	 */
	protected function fetchItem($soure, $group, $item_def=array(), $index=0) {
	    $result = array();
	    $group_values = array_keys($group);
	    $key = $group_values[$index];
	    if($index == 0 ) {
	        // 默认值
	        $item_def = array();
	        foreach ($group_values as $f) {
	            $item_def[$f] = null;
	        }
	        if (!empty($group[$key]) && is_array($group[$key])) {
	            foreach ($group[$key] as $c) {
	                $item_def[$c] = 0;
	            }
	        }
	    }
	    foreach ($soure[$key . 'list']['buckets'] as $item) {
	
	        $time_format = floatval(self::$_TIMEFORMAT) >= 24 ? "Ymd" : "Ymd H:i";
	        if (isset($item['key_as_string']) && substr($item['key_as_string'],-1,1) == 'Z') {
	            $item['key_as_string'] = substr($item['key_as_string'],0,-1);
	        }
	        $item_def[$key] = isset($item['key_as_string']) ? date($time_format, strtotime($item['key_as_string'])) : $item['key'];
	        
	        if (isset($this->_column['pv'])) {
	            $item_def[$this->_column['pv']] = $item['doc_count'];
	        } else {
	            $item_def['pv'] = $item['doc_count'];
	        }
	
	
	        if (strstr($key,'geo')!==false) {
	            if (isset($item['from'])) {
	                $item_def['distance'] = array('from'=>floatval($item['from']),'to'=>floatval($item['to']));
	            } else {
	                $lat = ($item['cell']['bounds']['top_left']['lat'] + $item['cell']['bounds']['bottom_right']['lat']) / 2;
	                $lon = ($item['cell']['bounds']['top_left']['lon'] + $item['cell']['bounds']['bottom_right']['lon']) / 2;
	                $item_def['geo'] = array('lat'=>floatval($lat),'lon'=>floatval($lon));
	            }
	        }
	        if (!empty($group[$key]) && is_array($group[$key])) {
	            foreach ($group[$key] as $c => $cv) {
	                if ($c != 'pv' && $cv != 'pv') {
	                    if (isset($item[$cv]['value_as_string']) && substr($item[$cv]['value_as_string'],-1,1) == 'Z') {
	                        @$item_def[$cv] = strtotime(substr($item[$cv]['value_as_string'],0,-1));
	                    } else {
    	                    @$item_def[$cv] = $item[$cv]['value'];
	                    }
	                }
	            }
	        }
	         
	        if ($index+1 == count($group)) {
	            $result[] = $item_def;
	        }
	        
	        if($index + 1 != count($group)) {
	            $sub_result = $this->fetchItem($item, $group, $item_def, $index + 1);
	            $result = array_merge($result, $sub_result);
	        }
	    }
	    return $result;
	}
	/**
	 * 获取树形结构数据
	 * @return NULL|multitype:Ambigous <unknown, multitype:NULL >
	 */
	public function fetchTree() {
	    if (! $this->_result) {
	        return null;
	    }
	    $data = json_decode($this->_result, true);
	    
	    if (!isset($data['aggregations'])) {
	        return null;
	    }
	    Debug::op_memory_set(__FILE__, __LINE__, 'fetchTree');
	    $result = $this->fetchItemTree($data['aggregations'], $this->_resultgroup);
	    Debug::op_memory_set(__FILE__, __LINE__, 'fetchTree');
	
	    return $result;
	}
	/**
	 * 获取树形结构数据 - 内部调用
	 * @param unknown_type $soure
	 * @param unknown_type $group
	 * @param unknown_type $item_def
	 * @param unknown_type $index
	 * @return multitype:Ambigous <unknown, multitype:NULL >
	 */
	protected function fetchItemTree($soure, $group, $item_def=array(), $index=0) {
	    $result = array();
	    $group_values = array_keys($group);
	    $key = $group_values[$index];
	    foreach ($soure[$key . 'list']['buckets'] as $item) {
	        if (isset($item['key_as_string'])) {
	            $item_def[$key] = $item['key_as_string'];
	            if ($key == 'ctime') {
	                $item_def[$key] = date("Ymd", substr(strtotime($item_def[$key]),0,10));
	            }
	        } else {
	            $item_def[$key] = $item['key'];
	        }
	        if(is_array($group[$key]) && !empty($group[$key])){
	            foreach ($group[$key] as $c) {
	                $item_def[$c] = $item[$c]['value'];
	            }
	        }
	
	        if (isset($this->_column['pv'])) {
	            $item_def[$this->_column['pv']] = $item['doc_count'];
	        } else {
	            $item_def['pv'] = $item['doc_count'];
	        }
	         
	        if (strstr($key,'geo')!==false) {
	            if (isset($item['from'])) {
	                $item_def['distance'] = array('from'=>floatval($item['from']),'to'=>floatval($item['to']));
	            } else {
	                $lat = ($item['cell']['bounds']['top_left']['lat'] + $item['cell']['bounds']['bottom_right']['lat']) / 2;
	                $lon = ($item['cell']['bounds']['top_left']['lon'] + $item['cell']['bounds']['bottom_right']['lon']) / 2;
	                $item_def['geo'] = array('lat'=>floatval($lat),'lon'=>floatval($lon));
	            }
	        }
	
	        $result[$key . '^' . $item['key']] = $item_def;
	        if($index + 1 != count($group)) {
	            $result[$key . '^' . $item['key']]['sub'] = $this->fetchItemTree($item, $group, $item_def, $index + 1);
	        }
	    }
	    return $result;
	}
	
		
	
	/**
	 * 获取返回结果集，需要的分组字段
	 * @return
	 */
	public function getRequestBody() {
	    return $this->post_content;
	}
	/**
	 * curl远程查询
	 * @param unknown_type $debug
	 * @return Es_Elasticsearch
	 */
	public function query($debug=false) {
	    $dsl = $this->getRequestBody();
	    $this->_result = Elasticsearchpool::FetchSourceCount($dsl, $this->_index, $this->_mapping, $this->_node);
	    return $this;
	}		
	/**
	 * 统计字段
	 * @param unknown_type $file
	 */
	static public function statColum($field, $showField="", $type="cardinality", $percentsOrValues=array(1,5,25,50,75,95,99)) {
	    $type_list = array(
            'avg' => array(
				'type' => 'avg',
                'name' => '平均值',
                'field' => '替换字段'
            ),
            'sum' => array(
				'type' => 'sum',
                'name' => '求和',
                'field' => '替换字段'
            ),
            'min' => array(
				'type' => 'min',
                'name' => '最小',
                'field' => '替换字段'
            ),
            'max' => array(
				'type' => 'max',
                'name' => '最大',
                'field' => '替换字段'
            ),
            'extended_stats' => array(
				'type' => 'extended_stats',
                'name' => '标准偏差',
                'field' => '替换字段'
            ),
            'cardinality' => array(
				'type' => 'cardinality',
                'name' => '去重统计',
                'field' => '替换字段'
            ),
	        'percentiles' => array(
				'type' => 'percentiles',
	            'name' => '百分位数',
	            'field' => '替换字段',
	            'percents' => '替换字段', 
	            'keyed' => false
	        ),
            'median' => array(
				'type' => 'percentiles',
                'name' => '中位数',
                'field' => '替换字段', 
                'percents' => '替换字段', 
                'keyed' => false
            ),
	        'pricelist_ranks' => array(
				'type' => 'percentiles',
                'name' => '数值分布比例',
	            //'desc' => '传入数据在整体数据中分布状态',
                'field' => '替换字段', 
                'values' => '替换字段',
                'keyed' => false
            ),
	        "range" => array(
	            'type' => 'range',
	            'name' => '区间查询',
	            //'desc' => '区间取值：大于等于    item  小于',
                "field" => '替换字段', 
                "ranges" => '替换字段',
            ),
        );
	    
	    if (isset($type_list[$type])) {
	        $type_list[$type]['field'] = $field; 
	    } else {
	        $type_list['cardinality']['field'] = $field;
	    }
	    $colum = $type_list[$type];
	    if (isset($colum['percents'])) {
	        $colum['percents'] = array_values($percentsOrValues);
	    }
	    if (isset($colum['values'])) {
	        $colum['values'] = array_values($percentsOrValues);
	    }
	    if (isset($colum['ranges'])) {
	        $colum['ranges'] = array();
	        $pre_v = array_shift($percentsOrValues);
	        $colum['ranges'][] = array(
	            "to"=>$pre_v,
	        );
	        if (!empty($percentsOrValues)) {
    	        foreach ($percentsOrValues as $v) {
    	            $colum['ranges'][] = array(
    	                "to"=>$v,
    	                "from"=>$pre_v,
    	            );
    	            $pre_v = $v;
    	        }
	        }
	        $colum['ranges'][] = array(
	            "from"=>$pre_v,
	        );
	    }
	    unset($colum['name']);
	    if (!empty($showField)) {
	        $colum['show_field'] = $showField;
	    }
	    return $colum;
	}
	
}